/*-----------------------------------------------------------------------*/
/* Low level disk I/O module SKELETON for FatFs     (C)ChaN, 2019        */
/*-----------------------------------------------------------------------*/
/* If a working storage control module is available, it should be        */
/* attached to the FatFs via a glue function rather than modifying it.   */
/* This is an example of glue functions to attach various exsisting      */
/* storage control modules to the FatFs module with a defined API.       */
/*-----------------------------------------------------------------------*/

#include "diskio.h"		/* Declarations of disk functions */
#include <stdio.h>

#define SD_TIMEOUT 30 * 1000

SD_CardInfoType sdhd;

/*-----------------------------------------------------------------------*/
/* Get Drive Status                                                      */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status(BYTE pdrv)
{
  DSTATUS res;
  
  res = sd_diskio_status(&sdhd);
  sysdbg_sdio_info("sd_diskio_status:%d\r\n", res);
  
	return res;
}

/*-----------------------------------------------------------------------*/
/* Inidialize a Drive                                                    */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize(BYTE pdrv)
{
  DSTATUS res;
  
  sdio_init();
  
  res = sd_diskio_initialize(&sdhd);
  sysdbg_sdio_info("initialize res:%d\r\n", res);
  sysdbg_sdio_info("sd size:%dMB\r\n", ((sdhd.csd.DeviceSize+1)*512)/1024);
  
	return res;
}

/*-----------------------------------------------------------------------*/
/* Read Sector(s)                                                        */
/*-----------------------------------------------------------------------*/
DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count)
{
  uint8_t ret;

  if(sd_diskio_check_status_with_timeout(&sdhd, SD_TIMEOUT) != 0)
  {
    return RES_NOTRDY;
  }

  ret = sd_diskio_read_blocks(&sdhd, buff, sector, count);
  sysdbg_sdio_info("read sector_count:%d, res:%d\r\n", count, ret);

  switch(ret)
  {
    case 0:
      if(sd_diskio_check_status_with_timeout(&sdhd, SD_TIMEOUT) != 0)
      {
        return RES_ERROR;
      }
      return RES_OK;

    case 1:
    case 2:
      return RES_PARERR;

    default:
      return RES_ERROR;
  }
}

/*-----------------------------------------------------------------------*/
/* Write Sector(s)                                                       */
/*-----------------------------------------------------------------------*/
#if FF_FS_READONLY == 0
DRESULT disk_write(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count)
{
  uint8_t ret;

  if(sd_diskio_check_status_with_timeout(&sdhd, SD_TIMEOUT) != 0)
  {
    return RES_NOTRDY;
  }
  
  ret = sd_diskio_write_blocks(&sdhd, buff, sector, count);
  sysdbg_sdio_info("write sector_count:%d, res:%d\r\n", count, ret);
  
  switch(ret)
  {
    case 0:
      if(sd_diskio_check_status_with_timeout(&sdhd, SD_TIMEOUT) != 0)
      {
        return RES_ERROR;
      }
      return RES_OK;
    
    case 1:
    case 2:
      return RES_PARERR;
    
    default:
      return RES_ERROR;
  }
}
#endif

/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */
/*-----------------------------------------------------------------------*/
DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff)
{
  uint8_t ret;
  
  switch (cmd)
  {
    /* Make sure that no pending write process */
    case CTRL_SYNC:
      return RES_OK;
    
    /* Get number of sectors on the disk (DWORD) */
    case GET_SECTOR_COUNT:
      *(DWORD*)buff = sdhd.logical_block_nbr;
      return RES_OK;
    
    /* Get R/W sector size (WORD) */
    case GET_SECTOR_SIZE:
      *(WORD*)buff = sdhd.logical_block_size;
      return RES_OK;
    
    /* Get erase block size in unit of sector (DWORD) */
    case GET_BLOCK_SIZE:
      *(DWORD*)buff = sdhd.logical_block_size / SD_DEFAULT_BLOCK_SIZE;
      return RES_OK;
    
    case CTRL_TRIM:
      if(sd_diskio_check_status_with_timeout(&sdhd, SD_TIMEOUT) != 0)
      {
        return RES_ERROR;
      }
      
      ret = sd_diskio_erase(&sdhd, ((LBA_t*)buff)[0], ((LBA_t*)buff)[1]);
      sysdbg_sdio_info("erase res:%d\r\n", ret);
      switch(ret)
      {
        case 0:
          if(sd_diskio_check_status_with_timeout(&sdhd, SD_TIMEOUT) != 0)
          {
            return RES_ERROR;
          }
          return RES_OK;
        
        case 1:
        case 2:
          return RES_PARERR;
        
        default:
          return RES_ERROR;
      }
      return RES_ERROR;

    default:
      return RES_ERROR;
  }

  return RES_ERROR;
}

/*-----------------------------------------------------------------------*/
/* get the current time                                                  */
/*-----------------------------------------------------------------------*/
DWORD get_fattime(void)
{
  /* fixed time */
  return (DWORD)((44 << 25) | (10 << 21) | (31) << 16 | (0 << 11) | (0 << 5) | (0 >> 1));
}

void DMA2_Stream3_IRQHandler(void)
{
  sd_dma_rx_isr();
}

void DMA2_Stream6_IRQHandler(void)
{
  sd_dma_tx_isr();
}

void SDIO_IRQHandler(void)
{
  sd_sdio_isr_dma();
}
